﻿' Copyright © Microsoft Corporation.  All Rights Reserved.
' This code released under the terms of the 
' Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
' <copyright file="MediaPlayerAdaptiveSupport.vb" company="Microsoft">
'     Copyright © Microsoft Corporation. All rights reserved.
' </copyright>
' <summary>Implements the MediaPlayer class</summary>
' <author>Microsoft Expression Encoder Team</author>

Imports Microsoft.VisualBasic
Imports Microsoft.Expression.Encoder.PlugInLoader
Imports Microsoft.Expression.Encoder.PlugInMssCtrl
Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Globalization
Imports System.IO
Imports System.IO.IsolatedStorage
Imports System.Net
Imports System.Text
Imports System.Threading
Imports System.Windows
Imports System.Windows.Browser
Imports System.Windows.Controls
Imports System.Windows.Media

Namespace ExpressionMediaPlayer

	Partial Public Class MediaPlayer
		Inherits Control
		''' <summary>
		''' Filename of the smooth streaming support module
		''' </summary>
		Friend Const smoothStreamingXAPName As String = "SmoothStreaming.xap"
		''' <summary>
		''' Assembly name of the smooth streaming support module
		''' </summary>
		Friend Const smoothStreamingAssemblyName As String = "SmoothStreaming.dll"
		''' <summary>
		''' class name of the smooth streaming support object for online playback
		''' </summary>
		Friend Const smoothStreamingOnlineObjectName As String = "Microsoft.Expression.Encoder.AdaptiveStreaming.AdaptiveStreamingSource"
		''' <summary>
		''' class name of the smooth streaming support object for offline playback
		''' </summary>
		Friend Const smoothStreamingOfflineObjectName As String = "Microsoft.Expression.Encoder.AdaptiveStreaming.OfflineAdaptiveStreamingSource"

		''' <summary>
		''' The plugin in loader helper object that loads the Smoooth streamign support module and processes the assembly information
		''' </summary>
		Private m_smoothStreamingSourcePlugIn As PlugInLoader
		''' <summary>
		''' The smooth streaming MSS object 
		''' </summary>
		Private m_smoothStreamingObject As Object
		''' <summary>
		''' The URI of the manifest for the currently playing item 
		''' </summary>
		Private m_smoothStreamingItemManifestUri As Uri
		''' <summary>
		''' The offline bitrate in kbps of the currently playing item 
		''' </summary>
		Private m_smoothStreamingItemOfflineVideoBitrateInKbps As Long
		''' <summary>
		''' The base URI of the online player -- which is needed so the offline player construct the correct IsoUri StreamNames for loading the chunks 
		''' </summary>
		Private m_offlineBaseUri As Uri

		''' <summary>
		''' The current media stream source in use (null if not currently using one).
		''' </summary>
		Private m_mediaStreamSource As MediaStreamSource

		''' <summary>
		''' is the player in an offline state?
		''' </summary>
		''' 
		Friend ReadOnly Property IsOffline() As Boolean
			Get
				If Application.Current.InstallState = InstallState.Installed Then
					If Me.OfflineBaseUri IsNot Nothing Then
					Return True
					End If
				End If
				Return False
			End Get
		End Property
		''' <summary>
		''' Obtains the base URI of the online player -- stored in the special file in isloated storage 
		''' </summary>
		Public ReadOnly Property OfflineBaseUri() As Uri
			Get
				If Me.m_offlineBaseUri Is Nothing Then
					Try
						Dim isoStore = IsolatedStorageFile.GetUserStoreForApplication()
#If DEBUG Then
						Dim dirNames() As String = isoStore.GetDirectoryNames()
						For Each dirName As String In dirNames
							Debug.WriteLine("IsolatedStorage dirName=" & dirName)
						Next dirName
						Dim fileNames() As String = isoStore.GetFileNames()
						For Each fileName As String In fileNames
							Debug.WriteLine("IsolatedStorage fileName=" & fileName)
						Next fileName
#End If
						Dim streamReader = New StreamReader(isoStore.OpenFile(Playlist.downloadCompletedFilename, System.IO.FileMode.Open, System.IO.FileAccess.Read), Encoding.Unicode)
						Dim offlineBaseUrl As String = streamReader.ReadLine()
						Dim videoBitrateTakenOfflineText As String = streamReader.ReadLine()
						streamReader.Close()
						Me.m_offlineBaseUri = New Uri(offlineBaseUrl, UriKind.Absolute)
#If DEBUG Then
						Debug.WriteLine("OfflineBaseUri=" & offlineBaseUrl)
#End If
					Catch iso As IsolatedStorageException
						Debug.WriteLine("IsolatedStorageException attempting read the download complete file!:" & iso.Message)
						Me.ShowErrorMessage(iso.Message)
					End Try
				End If
				Return m_offlineBaseUri
			End Get
		End Property

		''' <summary>
		''' Gets the media stream source for the currently playing item, or null if not using one.
		''' </summary>
		Friend Property CurrentMediaStreamSource() As MediaStreamSource
			Get
				Return m_mediaStreamSource
			End Get
			Private Set(ByVal value As MediaStreamSource)
				m_mediaStreamSource = value
			End Set
		End Property

		''' <summary>
		''' Loads the SmoothStreaming support module from the host when running online
		''' </summary>
		Private Sub LoadSmoothStreamingModuleOnline()
			Debug.WriteLine("InitSmoothStreamingCore -- howdy!")

			Me.m_smoothStreamingSourcePlugIn = New PlugInLoader()
			AddHandler m_smoothStreamingSourcePlugIn.PlugInLoadCompleted, AddressOf SmoothStreamingLoaded
			Dim xap As New Uri(smoothStreamingXAPName, UriKind.Relative)
			Me.m_smoothStreamingSourcePlugIn.Load(xap, smoothStreamingAssemblyName)
		End Sub

		''' <summary>
		''' Callback that is called once the SmoothStreaming support module is downloaded form the host when running online
		''' </summary>
		Private Sub SmoothStreamingLoaded(ByVal sender As Object, ByVal e As OpenReadCompletedEventArgs)
			If e.Cancelled Then
				Debug.WriteLine("SmoothStreamingSupportLoadCompleted -- Cancelled")
			ElseIf e.Error IsNot Nothing Then
				Debug.WriteLine("SmoothStreamingSupportLoadCompleted -- Failed:" & e.Error.Message)
				If e.Error.InnerException IsNot Nothing Then
					Debug.WriteLine("SmoothStreamingSupportLoadCompleted -> " & e.Error.InnerException.Message)
				End If
			Else
				Dim startedOK As Boolean = False
				Try
					CreateSmoothStreamingObject()
					StartSmoothStreamingPlayback()
					startedOK = True
				Catch ise As IsolatedStorageException
					Me.ShowErrorMessage(ise.ToString())
				Finally
					If Not startedOK Then
						Me.OnButtonClickStop(Nothing, Nothing)
					End If
				End Try
			End If
		End Sub

		''' <summary>
		''' Loads the SmoothStreaming support module from isloated storage when running offline
		''' </summary>
		Private Sub LoadSmoothStreamingModuleOffline()
			Dim baseUri As Uri = Me.OfflineBaseUri
			If baseUri Is Nothing Then
				Debug.Assert(False, "app is actually offline!")
				Return
			End If
			Dim xapUriRel As New Uri(MediaPlayer.smoothStreamingXAPName,UriKind.Relative)
			Dim xapUri As New Uri(baseUri, xapUriRel)
			Dim xap = New IsoUri(xapUri)
			Dim streamName As String = xap.StreamName
			Debug.WriteLine("Attempting to load:" & xapUri.ToString() & " from:" & xap.StreamName)

			Dim isoStore = IsolatedStorageFile.GetUserStoreForApplication()
			If isoStore.FileExists(streamName) Then
				Dim xapStream As Stream = isoStore.OpenFile(streamName, System.IO.FileMode.Open, System.IO.FileAccess.Read)
				Me.m_smoothStreamingSourcePlugIn = New PlugInLoader()
				Me.m_smoothStreamingSourcePlugIn.LocalLoad(xapStream, MediaPlayer.smoothStreamingAssemblyName)
			Else
				Throw New IsolatedStorageException("Required file not found in isolated storage:" & MediaPlayer.smoothStreamingXAPName & " as " & streamName)
			End If
		End Sub

		''' <summary>
		''' Completely releases the smooth streaming support module when no longer needed (e.g. when new playlist item doesn't use smooth streaming)
		''' </summary>
		Friend Sub UnloadSmoothStreaming()
			ResetSmoothStreaming()
			m_smoothStreamingSourcePlugIn = Nothing
		End Sub

		''' <summary>
		''' Releases the current smooth streaming object after playing a smooth streaming item from the playlist.
		''' But leaves the Smooth Streaming support module in memory so subsequent sequential smooth streaming items in the playlist will start playback quickly
		''' </summary>
		Friend Sub ResetSmoothStreaming()
			Me.m_smoothStreamingObject = Nothing
		End Sub

		''' <summary>
		''' Initialize smooth streaming playback for a manifest item
		''' </summary>
		Friend Sub InitSmoothStreamingPlayback(ByVal playlistItem As PlaylistItem)
#If DEBUG Then
			Debug.WriteLine("InitSmoothStreamingPlayback")
#End If
			Me.ResetSmoothStreaming()
			Me.m_smoothStreamingItemManifestUri = playlistItem.MediaSource
			Me.m_smoothStreamingItemOfflineVideoBitrateInKbps = playlistItem.OfflineVideoBitrateInKbps

			If m_smoothStreamingSourcePlugIn Is Nothing Then
				If Me.IsOffline Then
					LoadSmoothStreamingModuleOffline()
				Else
					LoadSmoothStreamingModuleOnline()
				End If
			End If
			If m_smoothStreamingSourcePlugIn IsNot Nothing Then
				CreateSmoothStreamingObject()
				StartSmoothStreamingPlayback()
			End If
		End Sub

		''' <summary>
		''' Create a smooth streaming object (either online or offline)
		''' </summary>
		Private Sub CreateSmoothStreamingObject()
			Try
				If Me.IsOffline Then
					Me.m_smoothStreamingObject = Me.m_smoothStreamingSourcePlugIn.CreateObject(smoothStreamingOfflineObjectName)
				Else
					Me.m_smoothStreamingObject = Me.m_smoothStreamingSourcePlugIn.CreateObject(smoothStreamingOnlineObjectName)
				End If
			Catch fe As PlugInLoaderFailedException
				Debug.WriteLine("SmoothStreamingSupportLoadCompleted: Create Failed:" & fe.Message)
			End Try
		End Sub

		''' <summary>
		''' For offline playback form a useful IsoUri
		''' </summary>
		Friend Function MakeOfflineIsoUri(ByVal mediaUri As Uri) As IsoUri
			Dim isoUri As IsoUri
			If mediaUri.IsAbsoluteUri Then
				isoUri = New IsoUri(mediaUri)
			Else
				isoUri = New IsoUri(New Uri(Me.OfflineBaseUri, mediaUri))
			End If
			Return isoUri
		End Function

		''' <summary>
		''' Actually start playback once the support module is loaded and the smooth streaming object is created.
		''' </summary>
		Private Sub StartSmoothStreamingPlayback()
#If DEBUG Then
			Debug.WriteLine("StartSmoothStreamingPlayback")
#End If
			Dim pluginMssOffline As IPlugInMssOfflineSupport = Nothing
			Dim plugInMssCore As IPlugInMssCore = TryCast(Me.m_smoothStreamingObject, IPlugInMssCore)
			If plugInMssCore IsNot Nothing Then
				plugInMssCore.MediaElement = Me.m_mediaElement
				If Me.IsOffline Then
					pluginMssOffline = TryCast(Me.m_smoothStreamingObject, IPlugInMssOfflineSupport)
					If pluginMssOffline IsNot Nothing Then
						Dim manifestUri As Uri = Me.m_smoothStreamingItemManifestUri
						If Not manifestUri.IsAbsoluteUri Then
							manifestUri = New Uri(Me.OfflineBaseUri, Me.m_smoothStreamingItemManifestUri)
						End If

						Dim isoUri As New IsoUri(manifestUri)
						If isoUri.IsoFileExists Then
							plugInMssCore.ManifestUrl = manifestUri
							pluginMssOffline.ParseManifest(isoUri.Stream)
						Else
							Me.ShowErrorMessage("manifest missing from isolated storage!")
							Throw New IsolatedStorageException("Manifest missing from isolated storage!")
						End If
					End If
				Else
					' Construct an absolute uri
					Dim absoluteUri As Uri
					If Me.m_smoothStreamingItemManifestUri.IsAbsoluteUri OrElse HtmlPage.Document.DocumentUri Is Nothing Then
						absoluteUri = Me.m_smoothStreamingItemManifestUri
					Else
						absoluteUri = New Uri(HtmlPage.Document.DocumentUri, Me.m_smoothStreamingItemManifestUri)
					End If

					plugInMssCore.ManifestUrl = absoluteUri
				End If

				If Me.IsOffline AndAlso (pluginMssOffline IsNot Nothing) AndAlso (m_smoothStreamingItemOfflineVideoBitrateInKbps > 0) Then
					' Force heuristics to play the bitrate actually present in offline storage
					pluginMssOffline.SetOfflinePlaybackBitrateInKbps(MediaStreamType.Video, Me.m_smoothStreamingItemOfflineVideoBitrateInKbps)
				End If

				plugInMssCore.StartPlayback()

				' Display stats graph if this template has a slot for it.
				If Me.m_gridPlugIn IsNot Nothing Then
					Dim plugInGraph As IPlugInMssStatisticsGraph = TryCast(Me.m_smoothStreamingObject, IPlugInMssStatisticsGraph)
					If plugInGraph IsNot Nothing Then
						Me.m_gridPlugIn.Children.Clear() ' Remove all prior plug ins
						Me.m_gridPlugIn.Children.Add(plugInGraph.StatisticsGraph)
					End If
				End If
			End If
		End Sub
	End Class
End Namespace
